home *** CD-ROM | disk | FTP | other *** search
-
-
-
- 170
-
- CHAPTER 16 - LONG SIGNED MULTIPLICATION AND DIVISION
-
-
- Now that you have some subroutines under your belt, it is time to
- get back to multiple word arithmetic. This was put on the back
- burner because we needed to negate long numbers and it is more
- efficient to do that as a subroutine. First, let's negate a long
- number and then put the parts together.
-
- To negate a number you complement it, then add 1. It looks like
- this:
-
- NUMBER_LENGTH EQU 4
-
- variable1 dq ?
-
- mov si, offset variable1
- mov cx, NUMBER_LENGTH
- not_loop:
- not WORD PTR [si]
- add si, 2
- loop not_loop
-
- mov si, offset variable1
- mov cx, NUMBER_LENGTH
- stc ; set carry flag
- add_loop:
- adc WORD PTR [si], 0
- inc si
- inc si
- loop add_loop
-
- This is straightforward. First negate, then add 1. The first add
- will add 1 because the carry flag is set. If there is a carry
- out, it will be taken care of in the next word with ADC (we add
- nothing but the carry). We can make this more compact and
- efficient with:
-
- mov si, offset variable1
- mov cx, NUMBER_LENGTH
- stc ; set carry flag
- negate_loop:
- not WORD PTR [si]
- adc WORD PTR [si], 0
- inc si
- inc si
- loop negate_loop
-
- Neither NOT nor INC effect CF, the carry flag, so the correct CF
- value will be propagated through the whole long number.
-
-
- When we do negation during our multiplication, the multiplicand
- will be a 4 word negation while the result will be a 5 word
-
- ______________________
-
- The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
-
-
-
-
- Chapter 16 - Multiple Word Arithmetic III 171
- _________________________________________
-
- negation, so we will pass the length as a parameter. The call in
- C would look like this:
-
- negate_it ( &number, length ) ; {1}
-
- On entry, the stack will look like this:
-
- length bp + 6
- address bp + 4
- old IP bp + 2
- bp -> old BP bp + 0
-
- Here is the entire subroutine:
-
- ; - - - - - START SUBROUTINES BELOW THIS LINE
- negate_it proc near
-
- NUMBER_LENGTH EQU [bp+6]
- NUMBER_ADDRESS EQU [bp+4]
-
- push bp
- mov bp, sp
- PUSHREGS cx, si
-
-
- mov si, NUMBER_ADDRESS
- mov cx, NUMBER_LENGTH
- stc ; set carry flag
- negate_loop:
- not WORD PTR [si]
- adc WORD PTR [si], 0
- inc si
- inc si
- loop negate_loop
-
- POPREGS cx, si
- pop bp
- ret ; calling routine adjusts stack
-
- negate_it endp
-
- ; - - - - - END SUBROUTINES ABOVE THIS LINE
-
- Well, so far we have the negation routine and the unsigned
- multiplication and division routines. What else is necessary?
- Only the main program, and here it is for multiplication:
-
- ; - - - - - START DATA BELOW THIS LINE
- multiplicand dq ?
- multiplier dw ?
- result dt ?
- result_sign_flag db ?
- ; - - - - - END DATA ABOVE THIS LINE
-
- ____________________
-
- 1. For you non-C people, the '&' stands for the address.
-
-
-
-
- The PC Assembler Tutor 172
- ______________________
-
- ; - - - - - START CODE BELOW THIS LINE
- outer_loop:
- mov result_sign_flag, 0 ; assume positive
- mov ax, offset multiplicand
- call get_signed_8byte
-
- test WORD PTR multiplicand + 6, 8000h ; is it negative ?
- jz get_next_number
-
- mov ax,4 ; negate 4 word number
- push ax
- mov ax, offset multiplicand
- push ax
- call negate_it
- add sp, 4 ; clear 2 pushes off stack
- not result_sign_flag ; reverse sign of result
-
- get_next_number:
- call get_signed ; get signed multiplier
- mov multiplier, ax
- test ax, 8000h ; is it negative
- jz do_the_multiplication
-
- neg multiplier ; negate
- not result_sign_flag ; reverse sign of result
-
- do_the_multiplication:
- mov ax, offset result
- push ax
- mov ax, multiplier ; the number, not the address
- push ax
- mov ax, offset multiplicand
- push ax
- call multiply_it
- add sp, 6 ; clear 3 pushes off stack
-
- ; is the result negative?
- test result_sign_flag, 0FFh ; 1111 1111 mask
- jz print_it
-
- mov ax, 5 ; 5 word result
- push ax
- mov ax, offset result
- push ax
- call negate_it
- add sp, 4 ; clear 2 pushes off stack
-
- print_it:
- mov ax, WORD PTR result + 8 ; top two bytes
- call print_hex
- mov ax, offset result ; the rest of result
- call print_signed_8byte
-
- jmp outer_loop
- ; - - - - - END CODE ABOVE THIS LINE
-
- The driver routine gets an 8 byte signed number. If the number is
-
-
-
-
- Chapter 16 - Multiple Word Arithmetic III 173
- _________________________________________
-
- negative it negates the number (to make it positive) and switches
- the sign of the result_sign_flag. The sign flag will either be
- 00h for positive or FFh for negative. It then gets a two byte
- signed number. If it is negative, the routine negates it and
- switches the sign flag. At this point both numbers are positive,
- so it calls the unsigned multiplication routine. At the end, it
- checks the result_sign_flag to see if the result should be
- positive or negative. If it should be negative, the routine calls
- negate_it one more time. Finally, the routine prints the number.
- The hex portion will be 0000 for positive or FFFF for negative
- unless the value is larger than an 8 byte signed number can hold,
- at which point the value of the 8 byte signed number will be
- incorrect.
-
- Here's the unsigned multiplication routine which has been turned
- into a subroutine:
-
- ; - - - - -
- multiply_it proc near
-
- RESULT_ADDRESS EQU [bp+8]
- MULTIPLIER_VALUE EQU [bp+6]
- MULTIPLICAND_ADDRESS EQU [bp+4]
-
- push bp
- mov bp, sp
- PUSHREGS ax, bx, cx, dx, si, di
-
- mov si, MULTIPLICAND_ADDRESS ; load pointers
- mov bx, RESULT_ADDRESS
-
- mov cx, 4 ; number of words
- sub di,di ; clear di
-
- mult_loop:
- mov ax, [si] ; multiplicand to ax
- mul WORD PTR MULTIPLIER_VALUE
- add ax, di ; add high word from last multiplication
- jnc store_result
- inc dx
- store_result:
- mov [bx], ax ; store 1 word of result.
- mov di, dx ; save high word for next multiplication
- add si, 2 ; increment pointers
- add bx, 2
- loop mult_loop
-
- mov [bx], di ; move last word of result
-
- POPREGS ax, bx, cx, dx, si, di
- pop bp
- ret ; calling routine adjusts stack
-
- multiply_it endp
- ; - - - - - - - - - - -
-
- Draw a picture of the stack to verify that the EQU values are
-
-
-
-
- The PC Assembler Tutor 174
- ______________________
-
- correct. The multiplication and the negation subroutines go in
- the subroutine section of SUBTEMP1.ASM. The driver routine is the
- main routine. If you don't remember how this multiplication
- routine works, go back to the chapter on unsigned multiple word
- multiplication since the code is the same.
-
-
-
- DIVISION
-
- Division is the same situation. We need a driver routine, but the
- division itself will be the unsigned division. In division, the
- remainder is the same sign as the dividend, and the sign of the
- quotient is (dividend_sign XOR divisor_sign). If both signs are
- the same, the quotient is positive; if the signs are different
- the quotient is negative. Here's the driver routine:
-
- ; - - - - - START DATA BELOW THIS LINE
- dividend dq ?
- divisor dw ?
- quotient dq ?
- remainder dw ?
- quotient_sign_flag db ?
- remainder_sign_flag db ?
- ; - - - - - END DATA ABOVE THIS LINE
-
- ; - - - - - START CODE BELOW THIS LINE
- outer_loop:
- mov quotient_sign_flag, 0 ; assume positive
- mov remainder_sign_flag, 0
-
- mov ax, offset dividend
- call get_signed_8byte
- test WORD PTR (dividend + 6), 8000h ; is it negative?
- jz get_next_number
-
- mov ax,4 ; negate 4 word number
- push ax
- mov ax, offset dividend
- push ax
- call negate_it
- add sp, 4 ; adjust stack
- not quotient_sign_flag ; switch sign of quotient
- mov remainder_sign_flag, 0FFh ; remainder is negative
-
- get_next_number:
- call get_signed
- mov divisor, ax
- test ax, 8000h ; is it negative
- jz do_the_division
-
- neg divisor
- not quotient_sign_flag ; switch sign of quotient
-
- do_the_division:
- mov ax, offset remainder
- push ax
-
-
-
-
- Chapter 16 - Multiple Word Arithmetic III 175
- _________________________________________
-
- mov ax, offset quotient
- push ax
- mov ax, divisor ; the number, not the address
- push ax
- mov ax, offset dividend
- push ax
- call divide_it
- add sp, 8 ; clear 4 pushes off stack
-
- ; are the remainder and quotient negative?
- test remainder_sign_flag, 0FFh
- jz test_the_quotient
- neg remainder
-
- test_the_quotient:
- test quotient_sign_flag, 0FFh ; 1111 1111 mask
- jz print_it
-
- mov ax, 4 ; 4 word result
- push ax
- mov ax, offset quotient
- push ax
- call negate_it
- add sp, 4 ; clear 2 pushes off stack
-
- print_it:
- mov ax, offset quotient
- call print_signed_8byte
- mov ax, remainder
- call print_signed
-
- jmp outer_loop
- ; - - - - - END CODE ABOVE THIS LINE
-
- We get the dividend and check the sign. If it is negative, we (1)
- negate the number, (2) switch the sign of the quotient, and (3)
- set the remainder sign flag to negative. We get the divisor,
- check for negative; if it is negative we negate it and switch the
- sign of the quotient. We now have two unsigned numbers and do
- unsigned division. After division, both the quotient and
- remainder are adjusted for sign.
-
- The division routine is the same as the unsigned routine before
- except it is now a subroutine:
-
- ; - - - - - - - - ENTER SUBROUTINE BELOW THIS LINE
- divide_it proc near
-
- REMAINDER_ADDRESS EQU [bp+10]
- QUOTIENT_ADDRESS EQU [bp+8]
- DIVISOR_VALUE EQU [bp+6]
- DIVIDEND_ADDRESS EQU [bp+4]
-
- push bp
- mov bp, sp
- PUSHREGS ax, bx, cx, dx, si, di
-
-
-
-
-
- The PC Assembler Tutor 176
- ______________________
-
-
- mov si, DIVIDEND_ADDRESS
- mov bx, QUOTIENT_ADDRESS
- add si, 6 ; start at the top word
- add bx, 6
- mov di, WORD PTR DIVISOR_VALUE
- mov cx, 4 ; number of words
- sub dx, dx ; clear dx for first division
-
- division_loop:
- mov ax, [si] ; dividend word to ax
- div di
- mov [bx], ax ; word of result to quotient
- sub si, 2 ; decrement the pointers
- sub bx, 2
- loop division_loop
-
- mov bx, REMAINDER_ADDRESS ; store remainder
- mov [bx], dx
-
- POPREGS ax, bx, cx, dx, si, di
- pop bp
- ret ; calling routine adjusts the stack
-
- divide_it endp
-
- ; - - - - - - - - ENTER SUBROUTINE ABOVE THIS LINE
-
- Draw a picture of the stack to verify that the EQU statements are
- correct for a NEAR routine. The division and negation subroutines
- go in the SUBROUTINES section of SUBTEMP1.ASM. The driver is the
- main program. If you don't remember how this division works, go
- back to the division chapter and look it over. Try out a few
- numbers to make sure that it is working the way it should.
-
-
- DATA INTEGRITY
-
- One thing that may have been annoying some of you is that when
- the programs sent us numbers for multiplication and division we
- sometimes negated them, effectively changing the data in memory,
- but never changed them back when we were done. In an operational
- subroutine, you would have to do it differently. The logic would
- be:
-
- NUMBER NEGATIVE? no everything's o.k.
-
- yes
-
- make copy
- negate
- reset pointer
-
- If the number is positive we won't change it. If the number is
- negative, we make a copy, negate the copy and use the copy for
- the operation.
-
-